///
//  Point2D v 1.0
//  Russell Lowke, Febrauary 22nd 2011
//
//  Copyright (c) 2011 Lowke Media
//  see http://www.lowkemedia.com for more information
//
//  Permission is hereby granted, free of charge, to any person obtaining a 
//  copy of this software and associated documentation files (the "Software"), 
//  to deal in the Software without restriction, including without limitation 
//  the rights to use, copy, modify, merge, publish, distribute, sublicense, 
//  and/or sell copies of the Software, and to permit persons to whom the 
//  Software is furnished to do so, subject to the following conditions:
// 
//  The above copyright notice and this permission notice shall be included in 
//  all copies or substantial portions of the Software.
// 
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
//  IN THE SOFTWARE. 
//
//

(function(window) {

	function Point2D(x, 					// (number) x location
					 y) 					// (number) y location
	{
	    this.x = x;
	    this.y = y;
	    
	    ///
	    //  Test if another point has the same x and y as this one.
	    //
	    this.equal = function(other) {		// (Point2D) other pt being tested
	    	if (this.x === other.x && 
	    		this.y === other.y) {
	    		return true;
	    	}
	    	
	    	return false;
	    }
	    
	    ///
	    //  Adds two Point2Ds together or, if val is a Number, adds that
	    //  number to both x and y in this Point2D.
	    //
	    this.add = function(val) {			// (Point2D or number) to be added
	    	if (val instanceof Point2D) {
	            this.x += val.x;
	            this.y += val.y;
	    	} else if (typeof(val) === "number") {
	    		this.x += val;
	            this.y += val;
	    	} else {
				throw new Error("add() method of Point2D doesn't understand val parameter of " + val);
	        }
	        return this;
	    }
	    
	    ///
	    //  Subtracts a Point2D from this Point2D or, if val is a Number, 
	    //  subtracts that Number from both x and y in this Point2D.
	    this.subtract = function(val) {		// (Point2D or number) to be subtracted
	    	if (val instanceof Point2D) {
	            this.x -= val.x;
	            this.y -= val.y;
	    	} else if (typeof(val) === "number") {
	    		this.x -= val;
	            this.y -= val;
	    	} else {
				throw new Error("subtract() method of Point2D doesn't understand val parameter of " + val);
	        }
	        return this;
	    }
	    
	    ///
	    //  Multiplies two Point2Ds together or, if val is a Number, 
	    //  multiplies that Number to both x and y in this Point2D.
	    //
	    this.multiply = function(val) {		// (Point2D or number) to be multiplied
	    	if (val instanceof Point2D) {
	            this.x *= val.x;
	            this.y *= val.y;
	    	} else if (typeof(val) === "number") {
	    		this.x *= val;
	            this.y *= val;
	    	} else {
				throw new Error("multiply() method of Point2D doesn't understand val parameter of " + val);
	        }
	        return this;
	    }
	    
	    ///
	    //  Divides this Point2D by another or, if val is a Number, 
	    //  divides both x and y by that Number.
	    //
	    this.divide = function(val) {		// (Point2D or number) to be divided
	    	if (val instanceof Point2D) {
	            this.x /= val.x;
	            this.y /= val.y;
	    	} else if (typeof(val) === "number") {
	    		this.x /= val;
	            this.y /= val;
	    	} else {
				throw new Error("divide() method of Point2D doesn't understand val parameter of " + val);
	        }
	        return this;
	    }
	    
	    ///
	    //  Rounds both x and y values.
	    //
	    this.round = function() {
	        this.x = Math.round(this.x);
	        this.y = Math.round(this.y);
	    }
	    
	    ///
	    //  Calculates the distance between this point and another
	    //
	    this.distance = function(val) {		// (Point2D) pt distance is being checked between
	    	if (val instanceof Point2D) {
	    		return Math.sqrt(Math.pow(val.x - this.x, 2) + Math.pow(val.y - this.y, 2));
	    	} else {
	    		throw new Error("distance() method of Point2D doesn't understand val parameter of " + val);
	    	}
	    }
	    
	    ///
	    // Rotates this point around (0, 0) by an angle
	    //
	    this.rotate = function(angle) {		// (number) angle in degrees by which to rotate point
	        var theta = Point2D.degreesToRadians(angle);
	        var x = Math.cos(theta)*this.x + -Math.sin(theta)*this.y;
	        var y = Math.sin(theta)*this.x +  Math.cos(theta)*this.y;
	        
	        this.x = x;
	        this.y = y;
	        
	        return this;
	    }
	    
	    ///
	    //  Ensures neither x nor y are ever greater than limiting value
	    //
	    this.limit = function(val) {		// (number) limiting value
	        this.x %= val;
	        this.y %= val;
	    }

		///
	    //  Clones this Point2D, returning a safe copy that can be manipulated
	    //
	    this.clone = function() {
	        return new Point2D(this.x, this.y);
	    }
	    
	    ///
	    // gives the length to (0, 0)
	    //
	    this.getLength = function() {
	    	return Math.sqrt(Math.pow(this.x, 2) + Math.pow(this.y, 2))
	    }

	    this.toString = function() {
			return "[Point2D (x:" + this.x + ", y:" + this.y + ")]";
		}
		
	};
	
	///
	// Convert degrees to radians.
	// radians = degrees*PI/180
	//
	Point2D.degreesToRadians = function(degrees) {
		// precalculate PI/180 = 0.01745329251994
		return degrees*0.01745329251994;
	}
	
	window.Point2D = Point2D;
}(window));